home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Text⁄Files
/
File List 14
/
FileSearch.c
< prev
next >
Wrap
Text File
|
1990-09-14
|
21KB
|
696 lines
/*
FileList 1.4
"FileSearch.c"
Based on (and compatible with) the FileSearch library that was
included with "Capp's Prime Editor Construction Kit". Added StuffIt
(Classic and Deluxe) and Compactor archives support.
The following routines may be used to step through the directories of
one or more mounted volumes. The volumes may be MFS or HFS and, when
used properly, can handle both MFS and HFS volumes intermixed without
a lot of extra checking and can even be used if HFS is not running.
StuffIt archives can be recognized and be treated as directories.
*/
#include "FileSearch.h"
#include "Utilities.h"
#include "Main.h"
#define STUFFIT 1 /* This is a StuffIt archive */
#define COMPACTOR 2 /* This is a Compactor archive */
/* ----- StuffIt compression methods ------------------------------------ */
#define noComp 0 /* No compression */
#define rleComp 1 /* RLE compression */
#define lzwComp 2 /* LZW compression */
#define hufComp 3 /* Huffman compression */
#define encrypted 0x10 /* Bit set if encrypted */
#define directory 0x20 /* Bit set if this is a directory */
#define stop 0x21 /* End of directory */
/* ---- StuffIt archive header ------------------------------------------ */
typedef struct { /* 22 bytes */
OSType signature; /* = 'SIT!' -- for verification */
unsigned short numFiles; /* number of files in archive */
unsigned long arcLength; /* length of entire archive incl. hdr */
OSType signature2; /* = 'rLau' -- for verification */
unsigned char version; /* version number (1 or 2=deluxe) */
unsigned char reserved[7];
} sitHdr;
/* ----- StuffIt file header -------------------------------------------- */
typedef struct { /* 112 bytes */
unsigned char compRMethod; /* rsrc fork compression method */
unsigned char compDMethod; /* data fork compression method */
unsigned char fName[64]; /* a Str63 */
OSType fType; /* file type */
OSType fCreator; /* file creator */
unsigned short FndrFlags; /* copy of Finder flags */
unsigned long creationDate;
unsigned long modDate;
unsigned long rsrcLength; /* decompressed lengths */
unsigned long dataLength;
unsigned long compRLength; /* compressed lengths */
unsigned long compDLength;
unsigned short rsrcCRC; /* CRC of rsrc fork */
unsigned short dataCRC; /* CRC of data fork */
unsigned char rPad,dPad; /* pad bytes-valid for encr. entries */
unsigned char reserved[4]; /* invalid data for now */
unsigned short hdrCRC; /* CRC of file header */
} fileHdr;
/* ----- Compactor archive header --------------------------------------- */
typedef struct { /* 8 bytes */
unsigned char version; /* File format version code (1) */
unsigned char part; /* Segment number */
unsigned short arcID; /* Randomly generated archive ID nbr */
unsigned long offset; /* Directory offset in this segment */
} CompactorHdr;
#define SIZEOF_CompactorHdr 8
/* If the file is splitted into more than one segment "offset" is non-zero
only for the last segment, and zero for the other segments. */
/* ----- Compactor directory header ------------------------------------- */
typedef struct { /* 7 + note_length bytes (not aligned) */
unsigned char filler[4]; /* CRC for directory */
unsigned char count[2]; /* Entries (including folders) */
unsigned char arcNote[1]; /* Archive note (Pascal string) */
} CompactorDirHdr;
#define SIZEOF_CompactorDirHdr 7 /* sizeof() returns 8 ! */
/* ----- Compactor directory entry (after file name) -------------------- */
typedef struct { /* 45 bytes (not aligned) */
unsigned char flags; /* ? */
unsigned char data[4]; /* Offset to data */
unsigned char type[4]; /* File type */
unsigned char creator[4]; /* File creator */
unsigned char cdate[4]; /* Creation date */
unsigned char mdate[4]; /* Modifiaction date */
unsigned char filler[8]; /* ? */
unsigned char rsize[4]; /* Resource fork size */
unsigned char dsize[4]; /* Data fork size */
unsigned char crsize[4]; /* Compressed resource fork size */
unsigned char cdsize[4]; /* Compressed data fork size */
} CompactorFile;
#define SIZEOF_CompactorFile 45 /* sizeof() returns 46 ! */
/* ----- Static data ---------------------------------------------------- */
typedef struct { /* Element of nesting stack: */
long DirId; /* Directory ID */
short index; /* File index */
} ELEMENT;
typedef struct {
short MaxLevel; /* Maximal directory nesting level */
short CurrentLevel; /* Current nesting level */
short ArchiveLevel; /* To set end of archive */
short VolumeIndex; /* Current volume index */
Str255 VolumeName; /* Volume name */
Str255 FileName; /* File/directory name */
HVolumeParam Volume; /* Volume info */
CInfoPBRec Info; /* File/directory info */
short ArchiveRef; /* Archive file reference */
short ArchiveType; /* Archive type */
ELEMENT Stack[]; /* Nesting stack */
} DATA;
static DATA *Data = 0; /* Data block in application heap */
/* #define USECRC */ /* Undefine this symbol to gain 622 bytes */
#ifdef USECRC
/*
CRC computation used by Stuffit.
The logic for this method of calculating the CRC 16 bit polynomial
is taken from an article by David Schwaderer in the April 1985
issue of PC Tech Journal.
Not quite CCITT-CRC16... and definitely not xmodem CRC16.
*/
#define CrcInit 0x0000 /* Initial CRC value */
/* ----- CRC lookup table ----------------------------------------------- */
static short crctab[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
/* ----- CRC calculation ------------------------------------------------ */
static unsigned short CrcNew (
register unsigned short crcval, /* Current CRC value */
register Byte *buffer, /* Buffer (or part of) to verify */
register unsigned long count) /* Length of buffer */
{
while (count--)
crcval = (((crcval >> 8) & 0x00FF) ^
crctab[(crcval^(*buffer++)) & 0x00FF]);
return crcval;
}
#endif
/* ----- Close archive -------------------------------------------------- */
void CloseArchive (void)
{
register DATA *data = Data;
if (!data || !data->ArchiveRef)
return;
FSClose(data->ArchiveRef);
data->ArchiveRef = data->ArchiveType = 0;
}
/* ----- Check for archive ---------------------------------------------- */
/*
Type Creator
StuffIt document 'SIT!' 'SIT!'
AutoUnstuffIt application 'APPL' 'aust'
StuffIt deluxe 'SITD' 'SIT!'
Compactor document 'PACT' 'CPCT'
Self-extracting application 'APPL' 'EXTR'
*/
short IsArchive (register HFileInfo *f)
{
register DATA *data = Data;
register short ref;
register short type;
if (!data || data->ArchiveRef)
return 0;
/* Check for:
1. StuffIt archives (type == 'SIT!' or 'SITD') or AutoUnstuffIt
applications (type == 'APPL' and creator == 'aust')
2. Compactor archives (type == 'PACT') or self-extracting
applications (type == 'APPL' and creator == 'EXTR') */
if (f->ioFlFndrInfo.fdType == 'SIT!' ||
f->ioFlFndrInfo.fdType == 'SITD' ||
(f->ioFlFndrInfo.fdType == 'APPL' &&
f->ioFlFndrInfo.fdCreator == 'aust')) {
if (!Stuffit)
return 0; /* Not enabled */
type = STUFFIT;
} else if (f->ioFlFndrInfo.fdType == 'PACT' ||
(f->ioFlFndrInfo.fdType == 'APPL' &&
f->ioFlFndrInfo.fdCreator == 'EXTR')) {
if (!Compactor)
return 0; /* Not enabled */
type = COMPACTOR;
} else
return 0;
/* Open the archive */
{
HFileParam p;
FillMemory((Byte *)&p, sizeof(p), 0);
p.ioVRefNum = f->ioVRefNum;
p.ioDirID = CurrDir();
p.ioNamePtr = f->ioNamePtr;
if (PBHOpen(&p, FALSE))
return 0;
ref = p.ioFRefNum;
}
/* Read and verify archive header. Prepare archive walk thru */
switch (type) {
case STUFFIT:
{
sitHdr sit;
long count;
count = sizeof(sitHdr);
if (FSRead(ref, &count, &sit) || GetEOF(ref, &count) ||
sit.signature != 'SIT!' ||
sit.signature2 != 'rLau' ||
(sit.version != 1 && sit.version != 2) ||
sit.arcLength != count) {
FSClose(ref);
return 0;
}
}
break;
case COMPACTOR:
/* Read Compactor header and skip over file data. */
{
CompactorHdr hdr;
long count;
count = SIZEOF_CompactorHdr;
if (FSRead(ref, &count, &hdr) ||
hdr.version != 0x01 ||
!hdr.offset ||
SetFPos(ref, fsFromStart, hdr.offset)) {
FSClose(ref);
return 0;
}
count = 0;
}
/* Read directory header. */
{
CompactorDirHdr hdr;
long count;
count = SIZEOF_CompactorDirHdr;
if (FSRead(ref, &count, &hdr) ||
SetFPos(ref, fsFromMark, hdr.arcNote[0])) {
FSClose(ref);
return 0;
}
++data->CurrentLevel;
data->ArchiveLevel = data->CurrentLevel;
BlockMove(hdr.count,
&data->Stack[data->CurrentLevel].index, 2);
}
break;
default:
FSClose(ref);
return 0;
}
data->ArchiveRef = ref;
return data->ArchiveType = type;
}
/* ----- Get next file from StuffIt archive ----------------------------- */
static CInfoPBPtr NextStuffitFile (void)
{
register DATA *data = Data;
register HFileInfo *f;
register short err;
fileHdr file;
long count;
f = &data->Info.hFileInfo;
/* Read and verify file header */
count = sizeof(fileHdr);
if (err = FSRead(data->ArchiveRef, &count, &file))
goto done; /* eofErr */
#ifdef USECRC
if (file.hdrCRC !=
CrcNew(CrcInit, (Byte *)&file, sizeof(fileHdr) - 2)) {
err = ioErr;
goto done;
}
#endif
/* Interprete file header */
switch (file.compRMethod & stop) {
case directory: /* New directory */
f->ioFlAttrib = 0x10;
f->ioNamePtr = data->FileName;
BlockMove(file.fName, f->ioNamePtr, file.fName[0] + 1);
f->ioFlCrDat = file.creationDate;
f->ioFlMdDat = file.modDate;
break;
case stop: /* End of directory */
err = fnfErr;
break;
default: /* File */
/* Skip resource and data fork */
if (err = SetFPos(data->ArchiveRef, fsFromMark,
file.compRLength + file.compDLength))
goto done;
f->ioNamePtr = data->FileName;
BlockMove(file.fName, f->ioNamePtr, file.fName[0] + 1);
f->ioFlFndrInfo.fdType = file.fType;
f->ioFlFndrInfo.fdCreator = file.fCreator;
f->ioFlFndrInfo.fdFlags = file.FndrFlags;
f->ioFlCrDat = file.creationDate;
f->ioFlMdDat = file.modDate;
f->ioFlLgLen = file.rsrcLength;
f->ioFlRLgLen = file.dataLength;
break;
}
done:
data->Info.hFileInfo.ioResult = err;
return &data->Info;
}
/* ----- Get next file from Compactor archive --------------------------- */
static CInfoPBPtr NextCompactorFile (void)
{
register DATA *data = Data;
register HFileInfo *f;
register short err;
register Byte buffer[128 + SIZEOF_CompactorFile];
register Boolean folder;
long count;
f = &data->Info.hFileInfo;
if (data->CurrentLevel < data->ArchiveLevel) {
err = ioErr;
goto done;
}
/* End of directory ? */
if (data->Stack[data->CurrentLevel].index == 0) {
err = (data->CurrentLevel == data->ArchiveLevel) ?
eofErr : /* End of archive */
fnfErr; /* End of folder */
PopDir();
goto done;
}
for (err = data->ArchiveLevel; err <= data->CurrentLevel; ++err)
--(data->Stack[err].index);
/* Read file name length. */
count = 1;
if (err = FSRead(data->ArchiveRef, &count, buffer)) {
err = ioErr; /* eofErr */
goto done;
}
folder = (buffer[0] & 0x80) != 0;
buffer[0] &= 0x7F;
/* Read file name and directory file data */
count = buffer[0] + /* Name length */
(folder ?
2 : /* Folder entry */
SIZEOF_CompactorFile); /* File entry */
if (err = FSRead(data->ArchiveRef, &count, buffer + 1)) {
err = ioErr; /* eofErr */
goto done;
}
/* Copy name. */
BlockMove(buffer, f->ioNamePtr = data->FileName, buffer[0] + 1);
/* Copy directory file entry */
if (folder) { /* Folder */
f->ioFlAttrib = 0x10;
if (data->CurrentLevel == (data->MaxLevel - 1)) {
err = memFullErr;
goto done;
}
++data->CurrentLevel;
/* Number of entries (including folders) in this folder */
BlockMove(buffer + buffer[0] + 1,
&data->Stack[data->CurrentLevel].index, 2);
} else { /* File */
register CompactorFile *p = (CompactorFile *)(buffer+buffer[0]+1);
BlockMove(&p->type, &f->ioFlFndrInfo.fdType, sizeof(long));
BlockMove(&p->creator, &f->ioFlFndrInfo.fdCreator, sizeof(long));
BlockMove(&p->cdate, &f->ioFlCrDat, sizeof(long));
BlockMove(&p->mdate, &f->ioFlMdDat, sizeof(long));
BlockMove(&p->dsize, &f->ioFlLgLen, sizeof(long));
BlockMove(&p->rsize, &f->ioFlRLgLen, sizeof(long));
}
done:
data->Info.hFileInfo.ioResult = err;
return &data->Info;
}
/* ----- Get next file from archive ------------------------------------- */
CInfoPBPtr NextArchiveFile (void)
{
register DATA *data = Data;
if (!data || !data->ArchiveRef)
return 0;
FillMemory((Byte *)&data->Info.hFileInfo, sizeof(CInfoPBRec), 0);
switch (data->ArchiveType) {
case 1:
return NextStuffitFile();
case 2:
return NextCompactorFile();
default:
return 0;
}
}
/* ----- Initialize everything ------------------------------------------ */
/*
Allocates data block and an an internal stack for the use of the file
search routines. Returns TRUE if successful, FALSE otherwise. It must be
called before calling any other file search routines. Parameter is the
maximum nesting level of directories. If <= 0 then 128 will be used.
*/
Boolean FileSearchOpen (register short nLevels)
{
if (Data)
return FALSE; /* Data block already allocated! */
if (nLevels <= 0)
nLevels = 128; /* Use default of 128 nesting levels */
else
if (nLevels == 1)
++nLevels; /* At least 2 levels */
if (Data = (DATA *)NewPtrClear(sizeof(DATA) + nLevels*sizeof(ELEMENT)))
Data->MaxLevel = nLevels;
return Data != 0;
}
/* ----- Releases the storage allocated by FileSearchOpen --------------- */
void FileSearchClose (void)
{
if (Data) {
DisposPtr(Data);
Data = 0;
}
}
/* ----- Prepare volume walk thru --------------------------------------- */
/*
If VRefNum is zero, then things are initialzed internally so that each
subsequent call to NextVol will return each of the mounted volumes. The
contents of the HVolumeParam struct will be undefined. If VRefNum is
non-zero, then it may be a real VRefNum, a WDDirID, or a drive number.
If a drive number is given, then the VRefNum will be in ioVRefNum. If
a WDDirID is given, then the WDDirID will be in ioVRefNum (despite
HGetVInfo's forcing a real VRefNum to be there; this allows you to,
say, search all files in or below the current working directory. Note
that, if HFS is not running, then ioVSigWord will be zero and all
following fields will be undefined. When you pass a non-zero value
to FirstVol, calls to NextVol will yield undefined results.
*/
HVolumeParam *FirstVol (register short VRefNum)
{
register DATA *data = Data;
if (!data)
return 0;
if (VRefNum) {
data->CurrentLevel = -1;
data->Volume.ioNamePtr = data->VolumeName;
data->Volume.ioVRefNum = VRefNum;
data->Volume.ioVolIndex =
data->Volume.ioVSigWord = 0;
PBHGetVInfo(&data->Volume, FALSE);
if (VRefNum < 0)
data->Volume.ioVRefNum = VRefNum;
} else
data->VolumeIndex = 0;
return &data->Volume;
}
/* ----- Get next volume ------------------------------------------------ */
/*
Returns a pointer to the HVolumeParam info for the next mounted volume.
If there is no next volume, then the ioResult will contain nsvErr.
As noted above, ioVSigWord will be zero if HFS is not running; it will
contain 0xD2D7 if the volume is an MFS volume and HFS is running, and
it will contain 0x4244 if it is an HFS volume.
*/
HVolumeParam *NextVol (void)
{
register DATA *data = Data;
if (!data)
return 0;
data->CurrentLevel = -1;
data->Volume.ioNamePtr = data->VolumeName;
data->Volume.ioVolIndex = ++(data->VolumeIndex);
data->Volume.ioVSigWord = 0;
PBHGetVInfo(&data->Volume, FALSE);
return &data->Volume;
}
/* ----- Prepare file walk thru ----------------------------------------- */
/*
Initializes things internally so that each subsequent call to NextFile
will return each file/directory in the given volume/directory.
If DirID is zero, then the root directory of the volume last selected
with FirstVol or NextVol is used. If DirID is non-zero, then the
directory with that ID on the volume last selected with FirstVol or
NextVol will be used (if the volume is MFS, then the DirID will be
ignored). If FirstFile has been called before for the current
volume, then the current status of the search for that directory is
saved before starting the new directory search. The saved status may
be restored with PopDir (see below). Note that searches can be nested
no deeper than 128 levels. If an attempt is made to go beyond that,
then FirstFile will ignore it and the next call to NextFile will return
the next file at the current level.
*/
void FirstFile (register long DirID)
{
register DATA *data = Data;
register ELEMENT *p;
if (!data || data->CurrentLevel == (data->MaxLevel - 1))
return;
p = data->Stack + ++(data->CurrentLevel);
p->DirId = IsHFSVol(data->Volume) ? DirID : 0;
p->index = 0;
}
/* ----- Get next file -------------------------------------------------- */
/*
Returns a pointer to the file info for the next file in the current
directory. If there is no next file, then the ioResult will contain
fnfErr. If the current volume is an MFS volume, then the pointer
returned will actually be a pointer to a FileParam struct, which is
a subset of the CInfoPBRec struct.
*/
CInfoPBPtr NextFile (void)
{
register DATA *data = Data;
register ELEMENT *p;
register HFileInfo *f;
if (!data)
return 0;
f = &data->Info.hFileInfo;
p = data->Stack + data->CurrentLevel;
f->ioNamePtr = data->FileName;
f->ioVRefNum = data->Volume.ioVRefNum;
f->ioFDirIndex = ++(p->index);
f->ioDirID = p->DirId;
if (IsHFSVol(data->Volume))
PBGetCatInfo(&data->Info, FALSE);
else {
PBGetFInfo(&data->Info, FALSE);
f->ioFlAttrib &= 0xEF;
}
return &data->Info;
}
/* ----- Current volume ------------------------------------------------- */
/*
Returns the vRefNum of the volume last returned by FirstVol or NextVol.
*/
/*
short CurrVol (void)
{
return Data ? Data->Volume.ioVRefNum : 0;
}
*/
/* ----- Current level -------------------------------------------------- */
/*
Returns the number of "saved" directory searches; i.e., the level of
directory nesting, unless you've skipped one or more levels
deliberately.
*/
/*
short CurrLevel (void)
{
return Data ? Data->CurrentLevel : 0;
}
*/
/* ----- Current directory ---------------------------------------------- */
/*
Returns the DirID of the directory containing the file last returned by
NextFile. If the file is on an MFS volume, then a zero will be returned.
*/
long CurrDir (void)
{
register DATA *data = Data;
return data ? data->Stack[data->CurrentLevel].DirId : 0;
}
/* ----- Pop directory -------------------------------------------------- */
/*
Restores the most recently saved search status (saved with FirstFile).
If there are no saved searches left, PopDir returns FALSE; otherwise, it
returns TRUE.
*/
Boolean PopDir (void)
{
register DATA *data = Data;
if (!data || !data->CurrentLevel)
return FALSE;
--(data->CurrentLevel);
return TRUE;
}